#ifndef WEBSERVER_H
#define WEBSERVER_H


#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <csignal>
#include <cassert>

#include "epoller/epoller.h"
#include "http/http_connection.h"
#include "threadpool/threadpool.h"
#include "database_pool/mysql_conn_pool.h"
#include "timer/listed_timer.h"
#include "log/logger.h"

class WebServer {
public:
    WebServer(int port, int trig_mode, int thread_num, int timeout, int max_clients,
              int linger, int close_log, int log_write_async, int db_conn_num,
              const char *db_url, const uint32_t db_port, const char *db_name,
              const char *db_uname, const char *db_passwd)
    {
        MAX_CLIENT_FD = max_clients;
        m_is_stop = false;
        m_port = port;
        m_timeout = timeout;
        m_linger = linger;
//        m_src_dir = strcat(getcwd(nullptr, 256), "/resources/");
        m_src_dir = "/home/zql/Desktop/MyWebServer/resources";
        HttpConnection::m_src_dir = m_src_dir;
        HttpConnection::m_clients_cnt = 0;

        // 开启日志
        if (!close_log) {
            if (log_write_async) {
                Logger::get_instance()->init("./ServerLog", 1000, 1024, 100);
            } else {
                Logger::get_instance()->init("./ServerLog", 1000, 1024, 0);
            }
        }

        // Epoll
        m_epoller = new Epoller();

        // 请求处理线程池
        m_threadpool = new ThreadPool(thread_num);

        // 数据库连接池
        MySqlConnPool::get_instance()->init(db_url, db_port, db_name, db_uname, db_passwd, db_conn_num);

        // 计时器
        m_timers = new ListedTimer::SortedTimers();

        // 设置Epoll触发模式、配置服务端socket
        set_trig_mode(trig_mode);
        if (!init_socket()) {m_is_stop = true;}

        //
        if (!close_log) {
            if (m_is_stop) {
                LOG_ERROR("========== Server init error!==========");
            } else {
                LOG_INFO("========== Server init ==========");
                LOG_INFO("Port:%d, OpenLinger: %s", m_port, m_linger? "true":"false");
                LOG_INFO("Listen Mode: %s, OpenConn Mode: %s",
                         (m_listen_ev & EPOLLET ? "ET": "LT"),
                         (m_conn_ev & EPOLLET ? "ET": "LT"));
                LOG_INFO("Source Directory: %s", HttpConnection::m_src_dir);
                LOG_INFO("SqlConnPool Num: %d, ThreadPool num: %d", db_conn_num, thread_num);
            }
        }
    }

    ~WebServer(){
        close(m_listen_fd);
        m_is_stop = true;
        delete m_src_dir;
        delete m_threadpool;
        delete m_timers;
        delete m_epoller;
        MySqlConnPool::get_instance()->close_pool();
    }

    void run();


private:
    bool init_socket();
    void set_trig_mode(uint32_t trig_mode);

    void process_listen_event();
    void process_read_event(HttpConnection *client);
    void process_write_event(HttpConnection *client);
    bool process_exit_signal();

    void read_task(HttpConnection *client);
    void write_task(HttpConnection *client);
    void http_process(HttpConnection *client);

    void extend_time(HttpConnection *client);
    static void sent_error(HttpConnection *client, const char *info);
    void close_client(HttpConnection *client);

    static void term_sig_handler(int sig);
    static int set_fd_nonblock(int fd);

private:
    uint32_t MAX_CLIENT_FD;
    unordered_map<int, HttpConnection> m_http_clients;


    uint32_t m_port;
    int m_listen_fd;
    uint32_t m_listen_ev;
    uint32_t m_conn_ev;
    static int m_SIG_pipe[2];

    const char *m_src_dir;
    int m_timeout;
    bool m_linger;
    bool m_is_stop;

    Epoller *m_epoller;
    ListedTimer::SortedTimers *m_timers;
    ThreadPool *m_threadpool;

};


#endif
